home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 June: Reference Library / Dev.CD Jun 96 RL / Dev.CD Jun 96 RL.toast / What's New? / Development Kits / Apple Game Sprockets DR1 / Examples / GlyphaIII / G3Graphics.c < prev    next >
Encoding:
Text File  |  1996-04-24  |  43.3 KB  |  1,262 lines  |  [TEXT/MPCC]

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Graphics.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // I like to isolate all the graphic routines - put them in their own file.
  9. // This way all the thousands of Rect variables and Pixmaps have a place to go.
  10. // Anyway, this file contains all the drawing routines.
  11.  
  12. #include "G3Externs.h"
  13. #include <Palettes.h>
  14.  
  15. #define kUpperEyeHeight            100
  16. #define kLowerEyeHeight            200
  17. #define kNumLightningPts        8
  18. #define kMaxNumUpdateRects        32
  19.  
  20.  
  21. void QuickUnionRect (Rect *, Rect *, Rect *);
  22. void CheckPlayerWrapAround (void);
  23. void DrawHand (void);
  24. void DrawEye (void);
  25. void DrawPlayer (void);
  26. void CheckEnemyWrapAround (short);
  27. void DrawEnemies (void);
  28. void GlyphaCopyMask(
  29.     BitMap *srcMap,
  30.     BitMap *srcMask,
  31.     BitMap *dstMap,
  32.     Rect *srcRect,
  33.     Rect *maskRect,
  34.     Rect *destRect,
  35.     short copyMode,
  36.     RgnHandle rgnMask
  37. );
  38.  
  39.  
  40. Rect        backSrcRect, workSrcRect, obSrcRect, playerSrcRect;
  41. Rect        numberSrcRect, idleSrcRect, enemyWalkSrcRect, enemyFlySrcRect;
  42. Rect        obeliskRects[4], playerRects[11], numbersSrc[11], numbersDest[11];
  43. Rect        updateRects1[kMaxNumUpdateRects], updateRects2[kMaxNumUpdateRects];
  44. Rect        flameSrcRect, flameDestRects[2], flameRects[4], eggSrcRect;
  45. Rect        platformSrcRect, platformCopyRects[9], helpSrcRect, eyeSrcRect;
  46. Rect        helpSrc, helpDest, handSrcRect, handRects[2], eyeRects[4];
  47. Point        leftLightningPts[kNumLightningPts], rightLightningPts[kNumLightningPts];
  48. CGrafPtr    backSrcMap, workSrcMap, obeliskSrcMap, playerSrcMap, eyeSrcMap;
  49. CGrafPtr    numberSrcMap, idleSrcMap, enemyWalkSrcMap, enemyFlySrcMap;
  50. CGrafPtr    flameSrcMap, eggSrcMap, platformSrcMap, helpSrcMap, handSrcMap;
  51. GrafPtr        playerMaskMap, enemyWalkMaskMap, enemyFlyMaskMap, eggMaskMap;
  52. GrafPtr        handMaskMap, eyeMaskMap;
  53. RgnHandle    playRgn;
  54. short        numUpdateRects1, numUpdateRects2;
  55. Boolean        whichList, helpOpen, scoresOpen;
  56.  
  57. extern    handInfo    theHand;
  58. extern    eyeInfo        theEye;
  59. extern    prefsInfo    thePrefs;
  60. extern    playerType    thePlayer;
  61. extern    enemyType    theEnemies[];
  62. extern    Rect        enemyRects[24];
  63. extern    WindowPtr    mainWindow;
  64. extern    long        theScore, wasTensOfThousands;
  65. extern    short        livesLeft, levelOn, numEnemies;
  66. extern    Boolean        evenFrame;
  67.  
  68.  
  69. //==============================================================  Functions
  70. //--------------------------------------------------------------  DrawPlatforms
  71.  
  72. // This function draws all the platforms on the background pixmap and the…
  73. // work pixmap.  It needs to know merely how many of them to draw.
  74.  
  75. void DrawPlatforms (short howMany)
  76. {
  77.     if (howMany > 3)            // If there are more than 3 platforms…
  78.     {                            // Draw a platform to background pixmap.
  79.         CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
  80.                 &((GrafPtr)backSrcMap)->portBits, 
  81.                 &platformCopyRects[2], &platformCopyRects[7], srcCopy, playRgn);
  82.                                 // Draw a platform to work pixmap.
  83.         CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  84.                 &((GrafPtr)workSrcMap)->portBits, 
  85.                 &platformCopyRects[7], &platformCopyRects[7], srcCopy, playRgn);
  86.                                 // Add rectangle to update list to be drawn to screen.
  87.         AddToUpdateRects(&platformCopyRects[7]);
  88.                                 // Ditto for a second platform.
  89.         CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
  90.                 &((GrafPtr)backSrcMap)->portBits, 
  91.                 &platformCopyRects[4], &platformCopyRects[8], srcCopy, playRgn);
  92.         CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  93.                 &((GrafPtr)workSrcMap)->portBits, 
  94.                 &platformCopyRects[8], &platformCopyRects[8], srcCopy, playRgn);
  95.         AddToUpdateRects(&platformCopyRects[8]);
  96.     }
  97.     else                        // If there are 3 or less platforms…
  98.     {
  99.         CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
  100.                 &((GrafPtr)backSrcMap)->portBits, 
  101.                 &platformCopyRects[3], &platformCopyRects[7], srcCopy, playRgn);
  102.         CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  103.                 &((GrafPtr)workSrcMap)->portBits, 
  104.                 &platformCopyRects[7], &platformCopyRects[7], srcCopy, playRgn);
  105.         AddToUpdateRects(&platformCopyRects[7]);
  106.         
  107.         CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
  108.                 &((GrafPtr)backSrcMap)->portBits, 
  109.                 &platformCopyRects[5], &platformCopyRects[8], srcCopy, playRgn);
  110.         CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  111.                 &((GrafPtr)workSrcMap)->portBits, 
  112.                 &platformCopyRects[8], &platformCopyRects[8], srcCopy, playRgn);
  113.         AddToUpdateRects(&platformCopyRects[8]);
  114.     }
  115.     
  116.     if (howMany > 5)        // If there are more than 5 platforms…
  117.     {
  118.         CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
  119.                 &((GrafPtr)backSrcMap)->portBits, 
  120.                 &platformCopyRects[0], &platformCopyRects[6], srcCopy, playRgn);
  121.         CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  122.                 &((GrafPtr)workSrcMap)->portBits, 
  123.                 &platformCopyRects[6], &platformCopyRects[6], srcCopy, playRgn);
  124.         AddToUpdateRects(&platformCopyRects[6]);
  125.     }
  126.     else                    // If there are 5 or less platforms…
  127.     {
  128.         CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
  129.                 &((GrafPtr)backSrcMap)->portBits, 
  130.                 &platformCopyRects[1], &platformCopyRects[6], srcCopy, playRgn);
  131.         CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  132.                 &((GrafPtr)workSrcMap)->portBits, 
  133.                 &platformCopyRects[6], &platformCopyRects[6], srcCopy, playRgn);
  134.         AddToUpdateRects(&platformCopyRects[6]);
  135.     }
  136. }
  137.  
  138. //--------------------------------------------------------------  ScrollHelp
  139.  
  140. // This function scrolls the help screen.  You pass it a number of pixels…
  141. // to scroll up or down (positive or negative number).
  142.  
  143. void ScrollHelp (short scrollDown)
  144. {
  145.     OffsetRect(&helpSrc, 0, scrollDown);        // Move the source rectangle.
  146.     
  147.     if (helpSrc.bottom > 398)                    // Check to see we don't go too far.
  148.     {
  149.         helpSrc.bottom = 398;
  150.         helpSrc.top = helpSrc.bottom - 199;
  151.     }
  152.     else if (helpSrc.top < 0)
  153.     {
  154.         helpSrc.top = 0;
  155.         helpSrc.bottom = helpSrc.top + 199;
  156.     }
  157.                                                 // Draw "scrolled" help screen.
  158.     CopyToMainWindow(helpSrcMap, &helpSrc, &helpDest, srcCopy, 0L);
  159. }
  160.  
  161. //--------------------------------------------------------------  OpenHelp
  162.  
  163. // Bring up the help screen.  This is a kind of "wipe" or "barn door" effect.
  164.  
  165. void OpenHelp (void)
  166. {
  167.     Rect        wallSrc, wallDest;
  168.     short        i;
  169.     
  170.     SetRect(&helpSrc, 0, 0, 231, 0);    // Initialize source and destination rects.
  171.     helpDest = helpSrc;
  172.     OffsetRect(&helpDest, 204, 171);
  173.     
  174.     SetRect(&wallSrc, 0, 0, 231, 199);
  175.     OffsetRect(&wallSrc, 204, 171);
  176.     wallDest = wallSrc;
  177.     
  178.     for (i = 0; i < 199; i ++)            // Loop through 1 pixel at a time.
  179.     {
  180.         LogNextTick(1L);                // Speed governor.
  181.         helpSrc.bottom++;                // Grow help source rect.
  182.         helpDest.bottom++;                // Grow help dest as well.
  183.         wallSrc.bottom--;                // Shrink wall source.
  184.         wallDest.top++;                    // Shrink wall dest.
  185.         
  186.                                         // So, as the help graphic grows, the wall graphic…
  187.                                         // shrinks.  Thus it is as though the wall is…
  188.                                         // lifting up to expose the help screen beneath.
  189.         
  190.                                         // Copy slightly larger help screen.
  191.     CopyToMainWindow(helpSrcMap, &helpSrc, &helpDest, srcCopy, 0L);
  192.                                         // Copy slightly smaller wall graphic.
  193.     CopyToMainWindow(backSrcMap, &wallSrc, &wallDest, srcCopy, 0L);
  194.         
  195.         WaitForNextTick();                // Speed governor.
  196.     }
  197.     helpOpen = TRUE;                    // When done, set flag to indicate help is open.
  198. }
  199.  
  200. //--------------------------------------------------------------  CloseWall
  201.  
  202. // Close the wall over whatever screen is up (help screen or high scores).
  203. // Since the wall just comes down over the opening - covering whatever was beneath,…
  204. // it's simpler than the above function.
  205.  
  206. void CloseWall (void)
  207. {
  208. #if    !GENERATINGPOWERPC
  209.     Rect        wallSrc, wallDest;
  210.     short        i;
  211.     
  212.     SetRect(&wallSrc, 0, 0, 231, 0);    // Initialize source and dest rects.
  213.     wallDest = wallSrc;
  214.     OffsetRect(&wallDest, 204, 370);
  215.     OffsetRect(&wallSrc, 204, 171);
  216.     
  217.     for (i = 0; i < 199; i ++)            // Do it one pixel at a time.
  218.     {
  219.         wallSrc.bottom++;                // Grow bottom of wall source.
  220.         wallDest.top--;                    // Move down wall dest.
  221.                                         // Draw wall coming down.
  222.     CopyToMainWindow(backSrcMap, &wallSrc, &wallDest, srcCopy, 0L);
  223.     }                                    // Note, no speed governing (why bother?).
  224. #endif
  225. }
  226.  
  227. //--------------------------------------------------------------  OpenHighScores
  228.  
  229. // This function is practically identical to the OpenHelp().  The only real…
  230. // difference is that we must first draw all the high scores offscreen before…
  231. // lifting the wall to reveal them.
  232.  
  233. void OpenHighScores (void)
  234. {
  235.     RGBColor    theRGBColor, wasColor;
  236.     Rect        wallSrc, wallDest;
  237.     Rect        scoreSrc, scoreDest;
  238.     Str255        scoreStr;
  239.     short        i, scoreWide;
  240.     
  241.     SetRect(&scoreSrc, 0, 0, 231, 0);                // Initialize source and dest rects.
  242.     OffsetRect(&scoreSrc, 204, 171);
  243.     scoreDest = scoreSrc;
  244.     
  245.     SetRect(&wallSrc, 0, 0, 231, 199);
  246.     OffsetRect(&wallSrc, 204, 171);
  247.     wallDest = wallSrc;
  248.     
  249.     SetPort((GrafPtr)workSrcMap);                    // We'll draw scores to the work pixmap.
  250.     PaintRect(&wallSrc);                            // Paint it black.
  251.     
  252.     GetForeColor(&wasColor);                        // Save the foreground color.
  253.     
  254.     TextFont(geneva);                                // Use Geneva 12 point Bold font.
  255.     TextSize(12);
  256.     TextFace(bold);
  257.     
  258.     Index2Color(132, &theRGBColor);                    // Get the 132nd color in RGB form.
  259.     RGBForeColor(&theRGBColor);                        // Make this color the pen color.
  260.     MoveTo(scoreSrc.left + 36, scoreSrc.top + 20);    // Get pen in right position to draw.
  261.     DrawString("\pGlypha III High Scores");            // Draw the title.
  262.     
  263.     TextFont(geneva);                                // Use Geneva 9 point Bold font.
  264.     TextSize(9);
  265.     TextFace(bold);
  266.     
  267.     for (i = 0; i < 10; i++)                        // Walk through all 10 high scores.
  268.     {
  269.         Index2Color(133, &theRGBColor);                // Use color 133 (in palette).
  270.         RGBForeColor(&theRGBColor);
  271.         NumToString((long)i + 1L, scoreStr);        // Draw "place" (1, 2, 3, …).
  272.         MoveTo(scoreSrc.left + 8, scoreSrc.top + 40 + (i * 16));
  273.         DrawString(scoreStr);
  274.         
  275.         Index2Color(128, &theRGBColor);                // Use color 128 (from palette).
  276.         RGBForeColor(&theRGBColor);
  277.         MoveTo(scoreSrc.left + 32, scoreSrc.top + 40 + (i * 16));
  278.         DrawString(thePrefs.highNames[i]);            // Draw the high score name (Sue, …).
  279.         
  280.         Index2Color(164, &theRGBColor);                // Use color 164 (from palette).
  281.         RGBForeColor(&theRGBColor);
  282.         NumToString(thePrefs.highScores[i], scoreStr);
  283.         scoreWide = StringWidth(scoreStr);            // Right justify.
  284.         MoveTo(scoreSrc.left + 191 - scoreWide, scoreSrc.top + 40 + (i * 16));
  285.         DrawString(scoreStr);                        // Draw the high score (12,000, …).
  286.         
  287.         Index2Color(134, &theRGBColor);                // Use color 134 (from palette).
  288.         RGBForeColor(&theRGBColor);
  289.         NumToString(thePrefs.highLevel[i], scoreStr);
  290.         scoreWide = StringWidth(scoreStr);            // Right justify.
  291.         MoveTo(scoreSrc.left + 223 - scoreWide, scoreSrc.top + 40 + (i * 16));
  292.         DrawString(scoreStr);                        // Draw highest level (12, 10, …).
  293.     }
  294.     
  295.     RGBForeColor(&wasColor);                        // Restore foreground color.
  296.  
  297.     SetPort((GrafPtr)mainWindow);
  298.  
  299.     for (i = 0; i < 199; i ++)                        // Now the standard scroll functions.
  300.     {
  301.         LogNextTick(1L);
  302.         scoreSrc.bottom++;
  303.         scoreDest.bottom++;
  304.         wallSrc.bottom--;
  305.         wallDest.top++;
  306.         
  307.     CopyToMainWindow(workSrcMap, &scoreSrc, &scoreDest, srcCopy, 0L);
  308.         
  309.     CopyToMainWindow(backSrcMap, &wallSrc, &wallDest, srcCopy, 0L);
  310.         
  311.         WaitForNextTick();
  312.     }
  313.     
  314.     scoresOpen = TRUE;                                // Flag that the scores are up.
  315. }
  316.  
  317. //--------------------------------------------------------------  UpdateLivesNumbers
  318.  
  319. // During a game, this function is called to reflect the current number of lives.
  320. // This is "lives remaining", so 1 is subtracted before displaying it to the screen.
  321. // The lives is "wrapped around" after 99.  So 112 lives will display as 12.  It's…
  322. // a lot easier to handle numbers this way (it beats a recursive function that might…
  323. // potentially draw across the entire screen.
  324.  
  325. void UpdateLivesNumbers (void)
  326. {
  327.     short        digit;
  328.     
  329.     digit = (livesLeft - 1) / 10;        // Get the "10's" digit.
  330.     digit = digit % 10L;                // Keep it less than 10 (0 -> 9).
  331.     if ((digit == 0) && ((livesLeft - 1) < 10))
  332.         digit = 10;                        // Use a "blank" space if zero and less than 10.
  333.                                         // Draw digit.
  334.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  335.             &((GrafPtr)backSrcMap)->portBits, 
  336.             &numbersSrc[digit], &numbersDest[0], srcCopy, 0L);
  337.  
  338.     CopyToMainWindow(backSrcMap, &numbersDest[0], &numbersDest[0], srcCopy, 0L);
  339.     
  340.     digit = (livesLeft - 1) % 10;        // Get 1's digit.
  341.                                         // Draw digit.
  342.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  343.             &((GrafPtr)backSrcMap)->portBits, 
  344.             &numbersSrc[digit], &numbersDest[1], srcCopy, 0L);
  345.  
  346.     CopyToMainWindow(backSrcMap, &numbersDest[1], &numbersDest[1], srcCopy, 0L);
  347. }
  348.  
  349. //--------------------------------------------------------------  UpdateScoreNumbers
  350.  
  351. // This function works just like the above function.  However, we allow the…
  352. // score to go to 6 digits (999,999) before rolling over.  Note however, that…
  353. // in both the case of the score, number of lives, etc., the game does in fact…
  354. // keep track of the "actual" number.  It is just that only so many digits are…
  355. // being displayed.
  356.  
  357. void UpdateScoreNumbers (void)
  358. {
  359.     long        digit;
  360.     
  361.     digit = theScore / 100000L;        // Get "hundreds of thousands" digit.
  362.     digit = digit % 10L;            // Clip off anything greater than 9.
  363.     if ((digit == 0) && (theScore < 1000000L))
  364.         digit = 10;                    // Use blank space if zero.
  365.                                     // Draw digit.
  366.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  367.             &((GrafPtr)backSrcMap)->portBits, 
  368.             &numbersSrc[digit], &numbersDest[2], srcCopy, 0L);
  369.  
  370.     CopyToMainWindow(backSrcMap, &numbersDest[2], &numbersDest[2], srcCopy, 0L);
  371.     
  372.     digit = theScore / 10000L;        // Get "tens of thousands" digit.
  373.     if (digit > wasTensOfThousands)    // Check for "extra life" here.
  374.     {
  375.         livesLeft++;                // Increment number of lives.
  376.         UpdateLivesNumbers();        // Reflect new lives on screen.
  377.         wasTensOfThousands = digit;    // Note that life was given.
  378.     }
  379.     digit = digit % 10L;            // Clip off anything greater than 9.
  380.     if ((digit == 0) && (theScore < 100000L))
  381.         digit = 10;                    // Use blank space if zero.
  382.                                     // Draw digit.
  383.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  384.             &((GrafPtr)backSrcMap)->portBits, 
  385.             &numbersSrc[digit], &numbersDest[3], srcCopy, 0L);
  386.     CopyToMainWindow(backSrcMap, &numbersDest[3], &numbersDest[3], srcCopy, 0L);
  387.     
  388.     digit = theScore / 1000L;        // Handle "thousands" digit.
  389.     digit = digit % 10L;
  390.     if ((digit == 0) && (theScore < 10000L))
  391.         digit = 10;
  392.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  393.             &((GrafPtr)backSrcMap)->portBits, 
  394.             &numbersSrc[digit], &numbersDest[4], srcCopy, 0L);
  395.     CopyToMainWindow(backSrcMap, &numbersDest[4], &numbersDest[4], srcCopy, 0L);
  396.     
  397.     digit = theScore / 100L;        // Handle 100's digit.
  398.     digit = digit % 10L;
  399.     if ((digit == 0) && (theScore < 1000L))
  400.         digit = 10;
  401.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  402.             &((GrafPtr)backSrcMap)->portBits, 
  403.             &numbersSrc[digit], &numbersDest[5], srcCopy, 0L);
  404.     CopyToMainWindow(backSrcMap, &numbersDest[5], &numbersDest[5], srcCopy, 0L);
  405.     
  406.     digit = theScore / 10L;            // Handle 10's digit.
  407.     digit = digit % 10L;
  408.     if ((digit == 0) && (theScore < 100L))
  409.         digit = 10;
  410.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  411.             &((GrafPtr)backSrcMap)->portBits, 
  412.             &numbersSrc[digit], &numbersDest[6], srcCopy, 0L);
  413.     CopyToMainWindow(backSrcMap, &numbersDest[6], &numbersDest[6], srcCopy, 0L);
  414.     
  415.     digit = theScore % 10L;            // Handle 1's digit.
  416.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  417.             &((GrafPtr)backSrcMap)->portBits, 
  418.             &numbersSrc[digit], &numbersDest[7], srcCopy, 0L);
  419.     CopyToMainWindow(backSrcMap, &numbersDest[7], &numbersDest[7], srcCopy, 0L);
  420. }
  421.  
  422. //--------------------------------------------------------------  UpdateLevelNumbers
  423.  
  424. // Blah, blah, blah.  Just like the above functions but handles displaying the…
  425. // level the player is on.  We allow 3 digits here (up to 999) before wrapping.
  426.  
  427. void UpdateLevelNumbers (void)
  428. {
  429.     short        digit;
  430.     
  431.     digit = (levelOn + 1) / 100;        // Do 100's digit.
  432.     digit = digit % 10L;
  433.     if ((digit == 0) && ((levelOn + 1) < 1000))
  434.         digit = 10;
  435.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  436.             &((GrafPtr)backSrcMap)->portBits, 
  437.             &numbersSrc[digit], &numbersDest[8], srcCopy, 0L);
  438.     CopyToMainWindow(backSrcMap, &numbersDest[8], &numbersDest[8], srcCopy, 0L);
  439.     
  440.     digit = (levelOn + 1) / 10;            // Do 10's digit.
  441.     digit = digit % 10L;
  442.     if ((digit == 0) && ((levelOn + 1) < 100))
  443.         digit = 10;
  444.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  445.             &((GrafPtr)backSrcMap)->portBits, 
  446.             &numbersSrc[digit], &numbersDest[9], srcCopy, 0L);
  447.     CopyToMainWindow(backSrcMap, &numbersDest[9], &numbersDest[9], srcCopy, 0L);
  448.     
  449.     digit = (levelOn + 1) % 10;            // Do 1's digit.
  450.     CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
  451.             &((GrafPtr)backSrcMap)->portBits, 
  452.             &numbersSrc[digit], &numbersDest[10], srcCopy, 0L);
  453.     CopyToMainWindow(backSrcMap, &numbersDest[10], &numbersDest[10], srcCopy, 0L);
  454. }
  455.  
  456. //--------------------------------------------------------------  GenerateLightning
  457.  
  458. // This function takes a point (h and v) and then generates two lightning bolts…
  459. // (one from the tip of each obelisk) to the point.  It does this by generating…
  460. // a list of segments (as the lightning is broken up into segements).  The drawing…
  461. // counterpart to this function will draw a line connecting these segements (a sort…
  462. // of dot-to-dot).
  463.  
  464. void GenerateLightning (short h, short v)
  465. {
  466.     #define kLeftObeliskH        172
  467.     #define kLeftObeliskV        250
  468.     #define kRightObeliskH        468
  469.     #define kRightObeliskV        250
  470.     #define kWander                16
  471.     
  472.     short        i, leftDeltaH, rightDeltaH, leftDeltaV, rightDeltaV, range;
  473.     
  474.     leftDeltaH = h - kLeftObeliskH;                // Determine the h and v distances between…
  475.     rightDeltaH = h - kRightObeliskH;            // obelisks and the target point.
  476.     leftDeltaV = v - kLeftObeliskV;
  477.     rightDeltaV = v - kRightObeliskV;
  478.     
  479.     for (i = 0; i < kNumLightningPts; i++)        // Calculate an even spread of points between…
  480.     {                                            // obelisk tips and the target point.
  481.         leftLightningPts[i].h = (leftDeltaH * i) / (kNumLightningPts - 1) + kLeftObeliskH;
  482.         leftLightningPts[i].v = (leftDeltaV * i) / (kNumLightningPts - 1) + kLeftObeliskV;
  483.         rightLightningPts[i].h = (rightDeltaH * i) / (kNumLightningPts - 1) + kRightObeliskH;
  484.         rightLightningPts[i].v = (rightDeltaV * i) / (kNumLightningPts - 1) + kRightObeliskV;
  485.     }
  486.     
  487.     range = kWander * 2 + 1;                    // Randomly scatter the points vertically…
  488.     for (i = 1; i < kNumLightningPts - 1; i++)    // but NOT the 1st or last points.
  489.     {
  490.         leftLightningPts[i].v += RandomInt(range) - kWander;
  491.         rightLightningPts[i].v += RandomInt(range) - kWander;
  492.     }
  493. }
  494.  
  495. //--------------------------------------------------------------  FlashObelisks
  496.  
  497. // This function either draws the obelisks "normal" or draws them inverted.
  498. // They're drawn "inverted" as if emanating energy or lit up by the bolts…
  499. // of lightning.  The flag "flashThem" specifies how to draw them.
  500.  
  501. void FlashObelisks (Boolean flashThem)
  502. {    
  503.     if (flashThem)        // Draw them "inverted"
  504.     {
  505.         CopyToMainWindow(obeliskSrcMap, &obeliskRects[0], &obeliskRects[2], srcCopy, 0L);
  506.         CopyToMainWindow(obeliskSrcMap, &obeliskRects[1], &obeliskRects[3], srcCopy, 0L);
  507.     }
  508.     else            // Draw them "normal"
  509.     {
  510.         CopyToMainWindow(backSrcMap, &obeliskRects[0], &obeliskRects[2], srcCopy, 0L);
  511.         CopyToMainWindow(backSrcMap, &obeliskRects[1], &obeliskRects[3], srcCopy, 0L);
  512.     }
  513. }
  514.  
  515. //--------------------------------------------------------------  StrikeLightning
  516.  
  517. // This function draws the lightning bolts.  The PenMode() is set to patXOr…
  518. // so that the lines are drawn inverted (colorwise).  This way, drawing the…
  519. // lightning twice over will leave no pixels disturbed.
  520.  
  521. void StrikeLightning (void)
  522. {
  523.     short        i;
  524.     
  525.     SetPort((GrafPtr)mainWindow);                // Draw straight to screen.
  526.     PenSize(1, 2);                                // Use a tall pen.
  527.     PenMode(patXor);                            // Use XOR mode.
  528.                                                 // Draw lightning bolts with inverted pen.
  529.     MoveTo(leftLightningPts[0].h, leftLightningPts[0].v);
  530.     for (i = 0; i < kNumLightningPts - 1; i++)    // Draw left lightning bolt.
  531.     {
  532.         MoveTo(leftLightningPts[i].h, leftLightningPts[i].v);
  533.         LineTo(leftLightningPts[i + 1].h - 1, leftLightningPts[i + 1].v);
  534.     }
  535.     
  536.     MoveTo(rightLightningPts[0].h, rightLightningPts[0].v);
  537.     for (i = 0; i < kNumLightningPts - 1; i++)    // Draw right lightning bolt.
  538.     {
  539.         MoveTo(rightLightningPts[i].h, rightLightningPts[i].v);
  540.         LineTo(rightLightningPts[i + 1].h - 1, rightLightningPts[i + 1].v);
  541.     }
  542.     
  543.     PenNormal();                                // Return pen to normal.
  544. }
  545.  
  546. //--------------------------------------------------------------  DumpBackToWorkMap
  547.  
  548. // Simple handy function that copies the entire background pixmap to the…
  549. // work pixmap.
  550.  
  551. void DumpBackToWorkMap (void)
  552. {
  553.     CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  554.             &((GrafPtr)workSrcMap)->portBits, 
  555.             &backSrcRect, &backSrcRect, srcCopy, 0L);
  556. }
  557.  
  558. //--------------------------------------------------------------  DumpMainToWorkMap
  559.  
  560. // Simple handy function that copies the entire screen to the…
  561. // work pixmap.
  562.  
  563. void DumpMainToWorkMap (void)
  564. {
  565.     CopyBits(&(((GrafPtr)mainWindow)->portBits), 
  566.             &((GrafPtr)workSrcMap)->portBits, 
  567.             &backSrcRect, &backSrcRect, srcCopy, 0L);
  568. }
  569.  
  570. //--------------------------------------------------------------  QuickUnionRect
  571.  
  572. // The Mac Toolbox gives you a UnionRect() function, but, like any Toolbox…
  573. // routine, if we can do it faster, we ought to.  Well, the function below…
  574. // is quick because (among other reasons), it assumes that the two rects…
  575. // being compared are the same size.
  576.  
  577. void QuickUnionRect (Rect *rect1, Rect *rect2, Rect *whole)
  578. {
  579.     if (rect1->left < rect2->left)        // See if we're to use rect1's left.
  580.     {
  581.         whole->left = rect1->left;
  582.         whole->right = rect2->right;
  583.     }
  584.     else                                // Use rect2's left.
  585.     {
  586.         whole->left = rect2->left;
  587.         whole->right = rect1->right;
  588.     }
  589.     
  590.     if (rect1->top < rect2->top)        // See if we're to use rect1's top.
  591.     {
  592.         whole->top = rect1->top;
  593.         whole->bottom = rect2->bottom;
  594.     }
  595.     else                                // Use rect2's top.
  596.     {
  597.         whole->top = rect2->top;
  598.         whole->bottom = rect1->bottom;
  599.     }
  600. }
  601.  
  602. //--------------------------------------------------------------  AddToUpdateRects
  603.  
  604. // This is an elegant way to handle game animation.  It has some drawbacks, but…
  605. // for ease of use, you may not be able to beat it.  The idea is that any time…
  606. // you want something drawn to the screen (copied from an offscreen pixmap to…
  607. // the screen) you pass the rectangle to this routine.  This routine then adds…
  608. // the rectangle to a growing list of these rectangles.  When the game reaches…
  609. // drawing phase, another routine copies all these rectangles.  It is assumed, …
  610. // nonetheless, that you have copied the little graphic offscreen that you want…
  611. // moved to the screen (the shpinx or whatever).  This routine will take care of…
  612. // drawing the shinx or whatever to the screen.
  613.  
  614. void AddToUpdateRects (Rect *theRect)
  615. {
  616.     if (whichList)        // We alternate every odd frame between two lists…
  617.     {                    // in order to hold a copy of rects from last frame.
  618.         if (numUpdateRects1 < (kMaxNumUpdateRects - 1))
  619.         {                // If we are below the maximum # of rects we can handle…
  620.                         // Add the rect to the list (array).
  621.             updateRects1[numUpdateRects1] = *theRect;
  622.                         // Increment the number of rects held in list.
  623.             numUpdateRects1++;
  624.                         // Do simple bounds checking (clip to screen).
  625.             if (updateRects1[numUpdateRects1].left < 0)
  626.                 updateRects1[numUpdateRects1].left = 0;
  627.             else if (updateRects1[numUpdateRects1].right > 640)
  628.                 updateRects1[numUpdateRects1].right = 640;
  629.             if (updateRects1[numUpdateRects1].top < 0)
  630.                 updateRects1[numUpdateRects1].top = 0;
  631.             else if (updateRects1[numUpdateRects1].bottom > 480)
  632.                 updateRects1[numUpdateRects1].bottom = 480;
  633.         }
  634.     }
  635.     else                // Exactly like the above section, but with the other list.
  636.     {
  637.         if (numUpdateRects2 < (kMaxNumUpdateRects - 1))
  638.         {
  639.             updateRects2[numUpdateRects2] = *theRect;
  640.             numUpdateRects2++;
  641.             if (updateRects2[numUpdateRects2].left < 0)
  642.                 updateRects2[numUpdateRects2].left = 0;
  643.             else if (updateRects2[numUpdateRects2].right > 640)
  644.                 updateRects2[numUpdateRects2].right = 640;
  645.             if (updateRects2[numUpdateRects2].top < 0)
  646.                 updateRects2[numUpdateRects2].top = 0;
  647.             else if (updateRects2[numUpdateRects2].bottom > 480)
  648.                 updateRects2[numUpdateRects2].bottom = 480;
  649.         }
  650.     }
  651. }
  652.  
  653. //--------------------------------------------------------------  CheckPlayerWrapAround
  654.  
  655. // This handles drawing wrap-around.  It is such that, when a player walks partly…
  656. // off the right edge of the screen, you see the player peeking through on the left…
  657. // side of the screen.  Since we can't (shouldn't) assume that the physical screen…
  658. // memory wraps around, we'll draw the right player clipped against the right edge…
  659. // of the screen and draw a SECOND PLAYER on the left edge (clipped to the left).
  660.  
  661. void CheckPlayerWrapAround (void)
  662. {
  663.     Rect        wrapRect, wasWrapRect, src;
  664.     
  665.     if (thePlayer.dest.right > 640)        // Player off right edge of screen.
  666.     {
  667.         thePlayer.wrapping = TRUE;        // Set "wrapping" flag.
  668.         wrapRect = thePlayer.dest;        // Start out with copy of player bounds.
  669.         wrapRect.left -= 640;            // Offset it a screenwidth to left.
  670.         wrapRect.right -= 640;
  671.                                         // Ditto with old location.
  672.         wasWrapRect = thePlayer.wasDest;
  673.         wasWrapRect.left -= 640;
  674.         wasWrapRect.right -= 640;
  675.         
  676.         if (thePlayer.mode == kBones)    // Draw second bones.
  677.         {
  678.             src = playerRects[thePlayer.srcNum];
  679.             src.bottom = src.top + thePlayer.frame;
  680.             GlyphaCopyMask(&((GrafPtr)playerSrcMap)->portBits, 
  681.                     &((GrafPtr)playerMaskMap)->portBits, 
  682.                     &((GrafPtr)workSrcMap)->portBits, 
  683.                     &src, &src, &wrapRect, srcCopy, playRgn);
  684.         }
  685.         else                            // Draw second player (not bones).
  686.         {
  687.             GlyphaCopyMask(&((GrafPtr)playerSrcMap)->portBits, 
  688.                     &((GrafPtr)playerMaskMap)->portBits, 
  689.                     &((GrafPtr)workSrcMap)->portBits, 
  690.                     &playerRects[thePlayer.srcNum], 
  691.                     &playerRects[thePlayer.srcNum], 
  692.                     &wrapRect, srcCopy, playRgn);
  693.         }
  694.         thePlayer.wrap = wrapRect;
  695.         AddToUpdateRects(&wrapRect);    // Add this to our list of update rects.
  696.     }
  697.     else if (thePlayer.dest.left < 0)    // Else if off the left edge…
  698.     {
  699.         thePlayer.wrapping = TRUE;        // Set "wrapping" flag.
  700.         wrapRect = thePlayer.dest;        // Start out with copy of player bounds.
  701.         wrapRect.left += 640;            // Offset it a screenwidth to right.
  702.         wrapRect.right += 640;
  703.                                         // Ditto with old location.
  704.         wasWrapRect = thePlayer.wasDest;
  705.         wasWrapRect.left += 640;
  706.         wasWrapRect.right += 640;
  707.         
  708.         if (thePlayer.mode == kBones)    // Draw second bones.
  709.         {
  710.             src = playerRects[thePlayer.srcNum];
  711.             src.bottom = src.top + thePlayer.frame;
  712.             GlyphaCopyMask(&((GrafPtr)playerSrcMap)->portBits, 
  713.                     &((GrafPtr)playerMaskMap)->portBits, 
  714.                     &((GrafPtr)workSrcMap)->portBits, 
  715.                     &src, &src, &wrapRect, srcCopy, playRgn);
  716.         }
  717.         else                            // Draw second player (not bones).
  718.         {
  719.             GlyphaCopyMask(&((GrafPtr)playerSrcMap)->portBits, 
  720.                     &((GrafPtr)playerMaskMap)->portBits, 
  721.                     &((GrafPtr)workSrcMap)->portBits, 
  722.                     &playerRects[thePlayer.srcNum], 
  723.                     &playerRects[thePlayer.srcNum], 
  724.                     &wrapRect, srcCopy, playRgn);
  725.         }
  726.         thePlayer.wrap = wrapRect;
  727.         AddToUpdateRects(&wrapRect);    // Add this to our list of update rects.
  728.     }
  729.     else
  730.         thePlayer.wrapping = FALSE;        // Otherwise, we're not wrapping.
  731. }
  732.  
  733. //--------------------------------------------------------------  DrawTorches
  734.  
  735. // This handles drawing the two torch's flames.  It chooses randomly from…
  736. // 4 torch graphics and draws right over the old torches.
  737.  
  738. void DrawTorches (void)
  739. {
  740.     short        who;
  741.     
  742.     who = RandomInt(4);
  743.     if (evenFrame)        // Only draw 1 torch - left on even frames…
  744.     {
  745.         CopyBits(&((GrafPtr)flameSrcMap)->portBits, 
  746.                 &((GrafPtr)workSrcMap)->portBits, 
  747.                 &flameRects[who], &flameDestRects[0], srcCopy, 0L);
  748.         AddToUpdateRects(&flameDestRects[0]);
  749.     }
  750.     else                // and draw the right torch on odd frames.
  751.     {                    // We do this even/odd thing for speed.  Why draw both?
  752.         CopyBits(&((GrafPtr)flameSrcMap)->portBits, 
  753.                 &((GrafPtr)workSrcMap)->portBits, 
  754.                 &flameRects[who], &flameDestRects[1], srcCopy, 0L);
  755.         AddToUpdateRects(&flameDestRects[1]);
  756.     }
  757. }
  758.  
  759. //--------------------------------------------------------------  DrawHand
  760.  
  761. // This function takes care of drawing the hand offscreen.  There are only…
  762. // two (well really three) choices - hand open, hand clutching (or no hand…
  763. // in which case both options are skipped).
  764.  
  765. void DrawHand (void)
  766. {
  767.     if (theHand.mode == kOutGrabeth)        // Fingers open.
  768.     {
  769.         GlyphaCopyMask(&((GrafPtr)handSrcMap)->portBits, 
  770.                 &((GrafPtr)handMaskMap)->portBits, 
  771.                 &((GrafPtr)workSrcMap)->portBits, 
  772.                 &handRects[0], 
  773.                 &handRects[0], 
  774.                 &theHand.dest, srcCopy, playRgn);
  775.         AddToUpdateRects(&theHand.dest);
  776.     }
  777.     else if (theHand.mode == kClutching)    // Fingers clenched.
  778.     {
  779.         GlyphaCopyMask(&((GrafPtr)handSrcMap)->portBits, 
  780.                 &((GrafPtr)handMaskMap)->portBits, 
  781.                 &((GrafPtr)workSrcMap)->portBits, 
  782.                 &handRects[1], 
  783.                 &handRects[1], 
  784.                 &theHand.dest, srcCopy, playRgn);
  785.         AddToUpdateRects(&theHand.dest);
  786.     }
  787. }
  788.  
  789. //--------------------------------------------------------------  DrawEye
  790.  
  791. // This function draws the eye (if it's floating about - stalking).
  792.  
  793. void DrawEye (void)
  794. {
  795.     if (theEye.mode == kStalking)
  796.     {
  797.         GlyphaCopyMask(&((GrafPtr)eyeSrcMap)->portBits, 
  798.                 &((GrafPtr)eyeMaskMap)->portBits, 
  799.                 &((GrafPtr)workSrcMap)->portBits, 
  800.                 &eyeRects[theEye.srcNum], 
  801.                 &eyeRects[theEye.srcNum], 
  802.                 &theEye.dest, srcCopy, playRgn);
  803.         AddToUpdateRects(&theEye.dest);
  804.     }
  805. }
  806.  
  807. //--------------------------------------------------------------  CopyAllRects
  808.  
  809. // This function goes through the list of "update rects" and copies from an…
  810. // offscreen pixmap to the main screen.  It is at this instant (during the…
  811. // execution of the below function) that the screen actually changes.  The…
  812. // whole rest of Glypha is, in essence, there only to lead up, ultimately, …
  813. // to this function.
  814.  
  815. void CopyAllRects (void)
  816. {
  817. #if GENERATINGPOWERPC
  818.     OSStatus    theError;
  819.     short        i;
  820.     
  821.     if (whichList)    // Every other frame, we alternate which list we use.
  822.     {                // Copy new graphics to screen (sphinxes, player, etc.).
  823. #if 0
  824.         for (i = 0; i < numUpdateRects1; i++)
  825.             InvalDisplayRect( gTheDisplay, &updateRects1[i] );
  826. #endif
  827. //        for (i = 0; i < numUpdateRects1; i++)
  828. //        {
  829. //            CopyBits(&((GrafPtr)workSrcMap)->portBits, 
  830. //                    &(((GrafPtr)mainWindow)->portBits), 
  831. //                    &updateRects1[i], &updateRects1[i], srcCopy, playRgn);
  832. //        }
  833.  
  834.                     // Patch up old graphics from last frame (old sphinx locations, etc.).
  835. #if 0
  836.         for (i = 0; i < numUpdateRects2; i++)
  837.             InvalDisplayRect( gTheDisplay, &updateRects2[i] );
  838. #endif
  839. //        for (i = 0; i < numUpdateRects2; i++)
  840. //        {
  841. //            CopyBits(&((GrafPtr)workSrcMap)->portBits, 
  842. //                    &(((GrafPtr)mainWindow)->portBits), 
  843. //                    &updateRects2[i], &updateRects2[i], srcCopy, playRgn);
  844. //        }
  845.  
  846.         // bring the dirty rectangles to the screen
  847.         SwapDisplayBuffers( gTheDisplay, NULL, 0 );
  848.  
  849.         // get the back buffer pointer
  850.         theError = GetDisplayBackBuffer( gTheDisplay, &workSrcMap );
  851.         if( theError )
  852.         {
  853.             DisposeDisplay( gTheDisplay );
  854.             RedAlert("\pUnable to get back buffer");
  855.         }
  856.  
  857.         // Clean up offscreen (get rid of sphinxes, etc.).
  858.         for (i = 0; i < numUpdateRects1; i++)
  859.         {
  860.             CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  861.                     &((GrafPtr)workSrcMap)->portBits, 
  862.                     &updateRects1[i], &updateRects1[i], srcCopy, playRgn);
  863.         }
  864.         
  865.         numUpdateRects2 = 0;    // Reset number of rects to zero.
  866.         whichList = !whichList;    // Toggle flag to use other list next frame.
  867.     }
  868.     else
  869.     {                // Copy new graphics to screen (sphinxes, player, etc.).
  870. #if 0
  871.         for (i = 0; i < numUpdateRects2; i++)
  872.             InvalDisplayRect( gTheDisplay, &updateRects2[i] );
  873. #endif
  874. //        for (i = 0; i < numUpdateRects2; i++)
  875. //        {
  876. //            CopyBits(&((GrafPtr)workSrcMap)->portBits, 
  877. //                    &(((GrafPtr)mainWindow)->portBits), 
  878. //                    &updateRects2[i], &updateRects2[i], srcCopy, playRgn);
  879. //        }
  880.                     // Patch up old graphics from last frame (old sphinx locations, etc.).
  881.  
  882. #if 0
  883.         for (i = 0; i < numUpdateRects1; i++)
  884.             InvalDisplayRect( gTheDisplay, &updateRects1[i] );
  885. #endif
  886. //        for (i = 0; i < numUpdateRects1; i++)
  887. //        {
  888. //            CopyBits(&((GrafPtr)workSrcMap)->portBits, 
  889. //                    &(((GrafPtr)mainWindow)->portBits), 
  890. //                    &updateRects1[i], &updateRects1[i], srcCopy, playRgn);
  891. //        }
  892.  
  893.         // bring the dirty rectangles to the screen
  894.         SwapDisplayBuffers( gTheDisplay, NULL, 0 );
  895.  
  896.         // get the back buffer pointer
  897.         theError = GetDisplayBackBuffer( gTheDisplay, &workSrcMap );
  898.         if( theError )
  899.         {
  900.             DisposeDisplay( gTheDisplay );
  901.             RedAlert("\pUnable to get back buffer");
  902.         }
  903.  
  904.         // Clean up offscreen (get rid of sphinxes, etc.).
  905.         for (i = 0; i < numUpdateRects2; i++)
  906.         {
  907.             CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  908.                     &((GrafPtr)workSrcMap)->portBits, 
  909.                     &updateRects2[i], &updateRects2[i], srcCopy, playRgn);
  910.         }
  911.         
  912.         numUpdateRects1 = 0;    // Reset number of rects to zero.
  913.         whichList = !whichList;    // Toggle flag to use other list next frame.
  914.     }
  915.         
  916. #else
  917.  
  918.     short        i;
  919.     
  920.     if (whichList)    // Every other frame, we alternate which list we use.
  921.     {                // Copy new graphics to screen (sphinxes, player, etc.).
  922.         for (i = 0; i < numUpdateRects1; i++)
  923.         {
  924.             CopyBits(&((GrafPtr)workSrcMap)->portBits, 
  925.                     &(((GrafPtr)mainWindow)->portBits), 
  926.                     &updateRects1[i], &updateRects1[i], srcCopy, playRgn);
  927.         }
  928.                     // Patch up old graphics from last frame (old sphinx locations, etc.).
  929.         for (i = 0; i < numUpdateRects2; i++)
  930.         {
  931.             CopyBits(&((GrafPtr)workSrcMap)->portBits, 
  932.                     &(((GrafPtr)mainWindow)->portBits), 
  933.                     &updateRects2[i], &updateRects2[i], srcCopy, playRgn);
  934.         }
  935.                     // Clean up offscreen (get rid of sphinxes, etc.).
  936.         for (i = 0; i < numUpdateRects1; i++)
  937.         {
  938.             CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  939.                     &((GrafPtr)workSrcMap)->portBits, 
  940.                     &updateRects1[i], &updateRects1[i], srcCopy, playRgn);
  941.         }
  942.         
  943.         numUpdateRects2 = 0;    // Reset number of rects to zero.
  944.         whichList = !whichList;    // Toggle flag to use other list next frame.
  945.     }
  946.     else
  947.     {                // Copy new graphics to screen (sphinxes, player, etc.).
  948.         for (i = 0; i < numUpdateRects2; i++)
  949.         {
  950.             CopyBits(&((GrafPtr)workSrcMap)->portBits, 
  951.                     &(((GrafPtr)mainWindow)->portBits), 
  952.                     &updateRects2[i], &updateRects2[i], srcCopy, playRgn);
  953.         }
  954.                     // Patch up old graphics from last frame (old sphinx locations, etc.).
  955.         for (i = 0; i < numUpdateRects1; i++)
  956.         {
  957.             CopyBits(&((GrafPtr)workSrcMap)->portBits, 
  958.                     &(((GrafPtr)mainWindow)->portBits), 
  959.                     &updateRects1[i], &updateRects1[i], srcCopy, playRgn);
  960.         }
  961.                     // Clean up offscreen (get rid of sphinxes, etc.).
  962.         for (i = 0; i < numUpdateRects2; i++)
  963.         {
  964.             CopyBits(&((GrafPtr)backSrcMap)->portBits, 
  965.                     &((GrafPtr)workSrcMap)->portBits, 
  966.                     &updateRects2[i], &updateRects2[i], srcCopy, playRgn);
  967.         }
  968.         
  969.         numUpdateRects1 = 0;    // Reset number of rects to zero.
  970.         whichList = !whichList;    // Toggle flag to use other list next frame.
  971.     }
  972.     
  973. #endif
  974. }
  975.  
  976. //--------------------------------------------------------------  DrawPlayer
  977.  
  978. // Although called "DrawPlayer()", this function actually does its drawing…
  979. // offscreen.  It is the above routine that will finally copy our offscreen…
  980. // work to the main screen.  Anyway, the below function draws the player…
  981. // offscreen in the correct position and state.
  982.  
  983. void DrawPlayer (void)
  984. {
  985.     Rect        src;
  986.     
  987.     if ((evenFrame) && (thePlayer.mode == kIdle))
  988.     {            // On even frames, we'll draw the "flashed" graphic of the player.
  989.                 // If you've played Glypha, you notice that the player begins a…
  990.                 // game flashing alternately between bones and a normal player.
  991.         GlyphaCopyMask(&((GrafPtr)idleSrcMap)->portBits, 
  992.                 &((GrafPtr)playerMaskMap)->portBits, 
  993.                 &((GrafPtr)workSrcMap)->portBits, 
  994.                 &idleSrcRect, 
  995.                 &playerRects[thePlayer.srcNum], 
  996.                 &thePlayer.dest, srcCopy, playRgn );
  997.     }
  998.     else if (thePlayer.mode == kBones)
  999.     {            // If the player is dead and a pile of bones…
  1000.         src = playerRects[thePlayer.srcNum];
  1001.         src.bottom = src.top + thePlayer.frame;
  1002.         GlyphaCopyMask(&((GrafPtr)playerSrcMap)->portBits, 
  1003.                 &((GrafPtr)playerMaskMap)->portBits, 
  1004.                 &((GrafPtr)workSrcMap)->portBits, 
  1005.                 &src, &src, &thePlayer.dest, srcCopy, playRgn );
  1006.     }
  1007.     else        // Else, if the player is neither idle nor dead…
  1008.     {
  1009.         GlyphaCopyMask(&((GrafPtr)playerSrcMap)->portBits, 
  1010.                 &((GrafPtr)playerMaskMap)->portBits, 
  1011.                 &((GrafPtr)workSrcMap)->portBits, 
  1012.                 &playerRects[thePlayer.srcNum], 
  1013.                 &playerRects[thePlayer.srcNum], 
  1014.                 &thePlayer.dest, srcCopy, playRgn );
  1015.     }
  1016.                 // Now we add the player to the update rect list.
  1017.     AddToUpdateRects(&thePlayer.dest);
  1018.                 // Record old locations.
  1019.     thePlayer.wasH = thePlayer.h;
  1020.     thePlayer.wasV = thePlayer.v;
  1021.                 // Record old bounds rect.
  1022.     thePlayer.wasDest = thePlayer.dest;
  1023. }
  1024.  
  1025. //--------------------------------------------------------------  CheckEnemyWrapAround
  1026.  
  1027. // This function both determines whether or not an enemy (sphinx) is wrapping around.
  1028. // If it is, the "second" wrapped-around enemy is drawn.
  1029.  
  1030. void CheckEnemyWrapAround (short who)
  1031. {
  1032.     Rect        wrapRect, wasWrapRect, src;
  1033.     
  1034.     if (theEnemies[who].dest.right > 640)    // Is enemy off the right edge of screen?
  1035.     {
  1036.         wrapRect = theEnemies[who].dest;    // Copy bounds.
  1037.         wrapRect.left -= 640;                // Offset bounds copy to left (one screen width).
  1038.         wrapRect.right -= 640;
  1039.                                             // Ditto with old bounds.
  1040.         wasWrapRect = theEnemies[who].wasDest;
  1041.         wasWrapRect.left -= 640;
  1042.         wasWrapRect.right -= 640;
  1043.                                             // Handle "egg" enemies.
  1044.         if ((theEnemies[who].mode == kFalling) || (theEnemies[who].mode == kEggTimer))
  1045.         {                                    // Handle "egg" enemy sinking into platform.
  1046.             if ((theEnemies[who].mode == kEggTimer) && (theEnemies[who].frame < 24))
  1047.             {
  1048.                 src = eggSrcRect;
  1049.                 src.bottom = src.top + theEnemies[who].frame;
  1050.             }
  1051.             else
  1052.                 src = eggSrcRect;
  1053.             GlyphaCopyMask(&((GrafPtr)eggSrcMap)->portBits, 
  1054.                     &((GrafPtr)eggMaskMap)->portBits, 
  1055.                     &((GrafPtr)workSrcMap)->portBits, 
  1056.                     &src, &src, &wrapRect, srcCopy, playRgn);
  1057.         }
  1058.         else                                // Otherwise, if enemy not an egg…
  1059.         {
  1060.             GlyphaCopyMask(&((GrafPtr)enemyFlySrcMap)->portBits, 
  1061.                     &((GrafPtr)enemyFlyMaskMap)->portBits, 
  1062.                     &((GrafPtr)workSrcMap)->portBits, 
  1063.                     &enemyRects[theEnemies[who].srcNum], 
  1064.                     &enemyRects[theEnemies[who].srcNum], 
  1065.                     &wrapRect, srcCopy, playRgn);
  1066.         }
  1067.         AddToUpdateRects(&wrapRect);        // Add bounds to update rect list.
  1068.     }
  1069.     else if (theEnemies[who].dest.left < 0)    // Check to see if enemy off left edge instead.
  1070.     {
  1071.         wrapRect = theEnemies[who].dest;    // Make a copy of enemy bounds.
  1072.         wrapRect.left += 640;                // Offset it right one screens width.
  1073.         wrapRect.right += 640;
  1074.                                             // Ditto with old bounds.
  1075.         wasWrapRect = theEnemies[who].wasDest;
  1076.         wasWrapRect.left += 640;
  1077.         wasWrapRect.right += 640;
  1078.         if ((theEnemies[who].mode == kFalling) || (theEnemies[who].mode == kEggTimer))
  1079.         {                                    // Blah, blah, blah.  This is just like the above.
  1080.             if ((theEnemies[who].mode == kEggTimer) && (theEnemies[who].frame < 24))
  1081.             {
  1082.                 src = eggSrcRect;
  1083.                 src.bottom = src.top + theEnemies[who].frame;
  1084.             }
  1085.             else
  1086.                 src = eggSrcRect;
  1087.             GlyphaCopyMask(&((GrafPtr)eggSrcMap)->portBits, 
  1088.                     &((GrafPtr)eggMaskMap)->portBits, 
  1089.                     &((GrafPtr)workSrcMap)->portBits, 
  1090.                     &src, &src, &wrapRect, srcCopy, playRgn);
  1091.         }
  1092.         else
  1093.         {
  1094.             GlyphaCopyMask(&((GrafPtr)enemyFlySrcMap)->portBits, 
  1095.                     &((GrafPtr)enemyFlyMaskMap)->portBits, 
  1096.                     &((GrafPtr)workSrcMap)->portBits, 
  1097.                     &enemyRects[theEnemies[who].srcNum], 
  1098.                     &enemyRects[theEnemies[who].srcNum], 
  1099.                     &wrapRect, srcCopy, playRgn);
  1100.         }
  1101.         AddToUpdateRects(&wrapRect);
  1102.     }
  1103. }
  1104.  
  1105. //--------------------------------------------------------------  DrawEnemies
  1106.  
  1107. // This function draws all the sphinx enemies (or eggs if they're in that state).
  1108. // It doesn't handle wrap-around (the above function does) but it does call it.
  1109.  
  1110. void DrawEnemies (void)
  1111. {
  1112.     Rect        src;
  1113.     short        i;
  1114.     
  1115.     for (i = 0; i < numEnemies; i++)    // Go through all enemies.
  1116.     {
  1117.         switch (theEnemies[i].mode)        // Handle the different modes as seperate cases.
  1118.         {
  1119.             case kSpawning:                // Spawning enemies are "growing" out of the platform.
  1120.             src = enemyRects[theEnemies[i].srcNum];
  1121.             src.bottom = src.top + theEnemies[i].frame;
  1122.             GlyphaCopyMask(&((GrafPtr)enemyWalkSrcMap)->portBits, 
  1123.                     &((GrafPtr)enemyWalkMaskMap)->portBits, 
  1124.                     &((GrafPtr)workSrcMap)->portBits, 
  1125.                     &src, &src, &theEnemies[i].dest,
  1126.                     srcCopy, playRgn);
  1127.             AddToUpdateRects(&theEnemies[i].dest);
  1128.                                         // Don't need to check wrap-around, when enemies…
  1129.                                         // spawn, they're never on the edge of screen.
  1130.             theEnemies[i].wasDest = theEnemies[i].dest;
  1131.             theEnemies[i].wasH = theEnemies[i].h;
  1132.             theEnemies[i].wasV = theEnemies[i].v;
  1133.             break;
  1134.             
  1135.             case kFlying:                // Flying enemies are air borne (gee).
  1136.             GlyphaCopyMask(&((GrafPtr)enemyFlySrcMap)->portBits, 
  1137.                     &((GrafPtr)enemyFlyMaskMap)->portBits, 
  1138.                     &((GrafPtr)workSrcMap)->portBits, 
  1139.                     &enemyRects[theEnemies[i].srcNum], &enemyRects[theEnemies[i].srcNum], 
  1140.                     &theEnemies[i].dest, srcCopy, playRgn);
  1141.             AddToUpdateRects(&theEnemies[i].dest);
  1142.             CheckEnemyWrapAround(i);    // I like the word "air bourne".
  1143.             theEnemies[i].wasDest = theEnemies[i].dest;
  1144.             theEnemies[i].wasH = theEnemies[i].h;
  1145.             theEnemies[i].wasV = theEnemies[i].v;
  1146.             break;
  1147.             
  1148.             case kWalking:                // Walking enemies are walking.  Enemies.
  1149.             GlyphaCopyMask(&((GrafPtr)enemyWalkSrcMap)->portBits, 
  1150.                     &((GrafPtr)enemyWalkMaskMap)->portBits, 
  1151.                     &((GrafPtr)workSrcMap)->portBits, 
  1152.                     &enemyRects[theEnemies[i].srcNum], &enemyRects[theEnemies[i].srcNum], 
  1153.                     &theEnemies[i].dest, srcCopy, playRgn);
  1154.             AddToUpdateRects(&theEnemies[i].dest);
  1155.                                         // Don't need to check wrap-around, enemies walk…
  1156.                                         // only briefly, and never off edge of screen.
  1157.             theEnemies[i].wasDest = theEnemies[i].dest;
  1158.             theEnemies[i].wasH = theEnemies[i].h;
  1159.             theEnemies[i].wasV = theEnemies[i].v;
  1160.             break;
  1161.             
  1162.             case kFalling:                // Falling enemies are in fact eggs!
  1163.             GlyphaCopyMask(&((GrafPtr)eggSrcMap)->portBits, 
  1164.                     &((GrafPtr)eggMaskMap)->portBits, 
  1165.                     &((GrafPtr)workSrcMap)->portBits, 
  1166.                     &eggSrcRect, &eggSrcRect, &theEnemies[i].dest,
  1167.                     srcCopy, playRgn );
  1168.             AddToUpdateRects(&theEnemies[i].dest);
  1169.             CheckEnemyWrapAround(i);    // Check for wrap around.
  1170.             theEnemies[i].wasDest = theEnemies[i].dest;
  1171.             theEnemies[i].wasH = theEnemies[i].h;
  1172.             theEnemies[i].wasV = theEnemies[i].v;
  1173.             break;
  1174.             
  1175.             case kEggTimer:                // These are idle, perhaps hatching, eggs.
  1176.             if (theEnemies[i].frame < 24)
  1177.             {                            // Below countdown = 24, the egss are sinking…
  1178.                 src = eggSrcRect;        // into the platform (hatch time!).
  1179.                 src.bottom = src.top + theEnemies[i].frame;
  1180.             }
  1181.             else
  1182.                 src = eggSrcRect;
  1183.             GlyphaCopyMask(&((GrafPtr)eggSrcMap)->portBits, 
  1184.                     &((GrafPtr)eggMaskMap)->portBits, 
  1185.                     &((GrafPtr)workSrcMap)->portBits, 
  1186.                     &src, &src, &theEnemies[i].dest,
  1187.                     srcCopy, playRgn );
  1188.             AddToUpdateRects(&theEnemies[i].dest);
  1189.             CheckEnemyWrapAround(i);    // Check for wrap around.
  1190.             theEnemies[i].wasDest = theEnemies[i].dest;
  1191.             theEnemies[i].wasH = theEnemies[i].h;
  1192.             theEnemies[i].wasV = theEnemies[i].v;
  1193.             break;
  1194.         }
  1195.     }
  1196. }
  1197.  
  1198. //--------------------------------------------------------------  DrawFrame
  1199.  
  1200. // This function is the "master" drawing function that calls all the above…
  1201. // routines.  It is called once per frame.
  1202.  
  1203. void DrawFrame (void)
  1204. {
  1205.     DrawTorches();                // Gee, draws the torches?
  1206.     DrawHand();                    // Draws the hand?
  1207.     DrawEye();                    // A clue to easing your documentation demands…
  1208.     DrawPlayer();                // is to use "smart" names for your functions.
  1209.     CheckPlayerWrapAround();    // Check for player wrap-around.
  1210.     DrawEnemies();                // Handle all sphinx-type enemy drawing.
  1211.     CopyAllRects();                // Put it all onscreen.
  1212. }
  1213.  
  1214. //-------------------------------------------------------- CopyToMainWindow
  1215.  
  1216. // Provides an easy way to copy bits to the main window.
  1217. void
  1218. CopyToMainWindow(
  1219.     CGrafPtr srcMap,
  1220.     Rect *srcRect,
  1221.     Rect *dstRect,
  1222.     short inMode,
  1223.     RgnHandle inClip
  1224. )
  1225. {
  1226.     CopyBits(&((GrafPtr)srcMap)->portBits, 
  1227.             &(((GrafPtr)mainWindow)->portBits), 
  1228.             srcRect, dstRect, inMode, inClip);
  1229. }
  1230.  
  1231. //---------------------------------------------------------  GlyphaCopyMask
  1232.  
  1233. // Conditionally calls CopyMask or CopyDeepMask depending on the
  1234. // Instruction Set Architecture (ISA).
  1235. void
  1236. GlyphaCopyMask(
  1237.     BitMap *srcMap,
  1238.     BitMap *srcMask,
  1239.     BitMap *dstMap,
  1240.     Rect *srcRect,
  1241.     Rect *maskRect,
  1242.     Rect *destRect,
  1243.     short copyMode,
  1244.     RgnHandle rgnMask
  1245. )
  1246. {
  1247. #if GENERATINGPOWERPC
  1248.     CopyDeepMask(srcMap, 
  1249.             srcMask, 
  1250.             dstMap, 
  1251.             srcRect, 
  1252.             maskRect, 
  1253.             destRect, copyMode, rgnMask );
  1254. #else
  1255.     CopyMask(srcMap, 
  1256.             srcMask, 
  1257.             dstMap, 
  1258.             srcRect, 
  1259.             maskRect, 
  1260.             destRect);
  1261. #endif
  1262. }